package org.jetlinks.pro.device.web;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.collections4.CollectionUtils;
import org.hswebframework.web.api.crud.entity.PagerResult;
import org.hswebframework.web.api.crud.entity.QueryOperation;
import org.hswebframework.web.api.crud.entity.QueryParamEntity;
import org.hswebframework.web.authorization.annotation.QueryAction;
import org.hswebframework.web.authorization.annotation.Resource;
import org.hswebframework.web.authorization.annotation.ResourceAction;
import org.hswebframework.web.authorization.annotation.SaveAction;
import org.jetlinks.core.message.DeviceMessage;
import org.jetlinks.core.message.MessageType;
import org.jetlinks.pro.assets.AssetsHolder;
import org.jetlinks.pro.device.entity.DeviceInstanceEntity;
import org.jetlinks.pro.device.entity.DeviceMessageTaskEntity;
import org.jetlinks.pro.device.enums.TaskState;
import org.jetlinks.pro.device.service.task.DeviceMessageTask;
import org.jetlinks.pro.device.service.task.DeviceMessageTaskService;
import org.jetlinks.pro.device.tenant.DeviceAsset;
import org.jetlinks.pro.device.tenant.DeviceAssetType;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/device/message/task")
@Tag(name = "设备批量指令管理")
@Resource(id = "device-msg-task", name = "设备批量指令管理")
@AllArgsConstructor
public class DeviceMessageTaskController {

    private final DeviceMessageTaskService taskService;

    @QueryAction
    @GetMapping("/_query")
    @QueryOperation(summary = "查询指令记录")
    @DeviceAsset(property = "deviceId")
    public Mono<PagerResult<DeviceMessageTask>> queryTask(@Parameter(hidden = true) QueryParamEntity paramEntity) {
        return taskService.queryPager(paramEntity);
    }

    @SaveAction
    @PostMapping
    @Operation(summary = "创建指令任务")
    public Mono<Integer> createTask(@RequestBody CreateTaskRequest request) {
        return AssetsHolder
            .injectQueryParam(request.toQuery(), DeviceAssetType.device, DeviceMessageTaskEntity::getDeviceId)
            .flatMap(query -> taskService.createTask(query, request::toMessage));
    }

    @PostMapping("/{id}")
    @ResourceAction(id = "execute", name = "下发指令")
    @Operation(summary = "下发指令")
    public Mono<Void> executeTask(@PathVariable String id) {

        return AssetsHolder
            .assertPermission(taskService.findTask(id), DeviceAssetType.device, DeviceMessageTask::getDeviceId)
            .flatMap(taskService::executeTask);
    }

    @PutMapping("/state/{state}")
    @SaveAction
    @QueryOperation(summary = "按查询条件修改状态")
    public Mono<Void> changeState(@PathVariable @Parameter(description = "状态") TaskState state,
                                  @Parameter(hidden = true) QueryParamEntity query) {

        return AssetsHolder
            .injectQueryParam(query, DeviceAssetType.device, DeviceMessageTaskEntity::getDeviceId)
            .flatMapMany(taskService::query)
            .map(DeviceMessageTask::getId)
            .buffer(200)
            .flatMap(list -> taskService.changeSate(list, state))
            .then();
    }

    @Getter
    @Setter
    public static class CreateTaskRequest {
        @Schema(description = "设备ID")
        private List<String> deviceId;

        @Schema(description = "产品ID")
        private String productId;

        @Schema(description = "按查询条件下发")
        private QueryParamEntity query;

        @Schema(description = "指令内容")
        private Map<String, Object> message;

        public QueryParamEntity toQuery() {
            if (query == null) {
                if (CollectionUtils.isEmpty(deviceId)&&StringUtils.isEmpty(productId)) {
                    throw new IllegalArgumentException("error.product_ID_cannot_be_empty");
                }
                return QueryParamEntity
                    .newQuery()
                    .when(CollectionUtils.isNotEmpty(deviceId), q -> q.in(DeviceInstanceEntity::getId, deviceId))
                    .when(CollectionUtils.isEmpty(deviceId), q -> q.is(DeviceInstanceEntity::getProductId, productId))
                    .getParam();
            }
            return query;
        }

        public DeviceMessage toMessage() {
            Assert.notEmpty(message,"error.message_format");
            return MessageType
                .<DeviceMessage>convertMessage(message)
                .filter(msg-> msg.getMessageType()!=MessageType.UNKNOWN)
                .orElseThrow(() -> new IllegalArgumentException("error.message_format"));
        }
    }
}
